﻿/* 
The MIT License (MIT)

Copyright (c) 2013 Tilo Szepan, Immo Wache

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Navigation;
using System.Collections.Generic;
using System.Collections;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace WPDialog
{
    public class Element
    {
        protected object _label;
        protected string _title;
        public virtual string Title
        {
            get { return _title; }
            set
            {
                _title = value;
                if (_label != null)
                {
                    if (_label is TextBlock) (_label as TextBlock).Text = _title;
                    else if (_label is Button) (_label as Button).Content = _title;
                }
            }
        }
        public Action<Element> Action { get; set; }
        protected UIElement _cell;
        public UIElement Cell
        {
            get
            {
                if (_cell == null) InitCell();
                return _cell;
            }
            protected set { _cell = value; }
        }
        public RootElement Parent { get; internal set; }
        public int Index { get { return Parent != null ? Parent.IndexOf(this) : -1; } }

        public Element() { }
        public Element(string title) : this(title, null) { }
        public Element(string title, Action<Element> action)
        {
            _title = title;
            Action = action;
        }

        protected virtual void InitCell()
        {
            var b = new Button();
            b.Content = Title;
            _label = b;
            b.Click += new RoutedEventHandler(Trigger);
            _cell = b;
        }
        public virtual void Trigger(object sender = null, RoutedEventArgs e = null)
        {
            if (Action != null) Action(this);
        }
    }

    public class EntryElement : Element
    {
        public string Value { get; set; }
        public bool SplitHorizontal { get; set; }
        public bool IsPassword { get; set; }
        public EntryElement(string title, string value, bool isPasswd)
            : base(title, null)
        {
            Value = value;
            IsPassword = isPasswd;
        }
        public EntryElement(string title, string value) : this(title, value, false) { }
        protected override void InitCell()
        {
            var p = new Grid();
            var l = new TextBlock();
            l.Text = Title;
            _label = l;
            if (SplitHorizontal)
            {
                p.ColumnDefinitions.Add(new ColumnDefinition());
                p.ColumnDefinitions.Add(new ColumnDefinition());
                l.TextAlignment = TextAlignment.Right;
                l.FontSize = 25;
                l.VerticalAlignment = VerticalAlignment.Center;
            }
            else
            {
                p.RowDefinitions.Add(new RowDefinition());
                p.RowDefinitions.Add(new RowDefinition());
                l.Margin = new Thickness(0, 10, 0, 0);
            }

            p.Children.Add(l);
            p.Children.Add(entry());
            _cell = p;
        }
        FrameworkElement entry()
        {
            FrameworkElement e;
            if (IsPassword)
            {
                var entry = new PasswordBox();
                entry.Password = Value ?? "";
                entry.PasswordChanged += delegate
                {
                    Value = entry.Password;
                    Trigger();
                };
                e = entry;
            }
            else
            {
                var entry = new TextBox();
                entry.Text = Value ?? "";
                entry.TextChanged += delegate // .LostFocus
                {
                    Value = entry.Text;
                    Trigger();
                };
                e = entry;
            }
            if (SplitHorizontal)
                Grid.SetColumn(e, 1);
            else
                Grid.SetRow(e, 1);

            return e;
        }
    }
    public enum SeparatorStyle
    {
        Header,
        Footer
    }
    public class SeparatorElement : Element
    {
        public TextAlignment Alignment { get; set; }
        public SeparatorStyle Style{get; set;}
        public SeparatorElement() { Style = SeparatorStyle.Header; }
        public SeparatorElement(string title) : this(title, SeparatorStyle.Header) { }
        public SeparatorElement(string title, SeparatorStyle style) : base(title, null)
        { Style = style; Alignment = TextAlignment.Center; }
        protected override void InitCell()
        {
            var l = new TextBlock();
            l.TextAlignment = Alignment;// TextAlignment.Center;
            l.Text = Title ?? "";
            _label = l;
            l.HorizontalAlignment = HorizontalAlignment.Stretch;
            l.Margin = Style == SeparatorStyle.Header ? new Thickness(0, 30, 0, 0) : new Thickness(0, 0, 0, 30);
            l.Foreground = (Brush)Application.Current.Resources["PhoneSubtleBrush"];
            _cell = l;
        }
    }
    public class SliderElement : Element
    {
        Slider _slider;
        public Slider Slider
        {
            get
            {
                if (_slider == null) InitCell();
                return _slider;
            }
        }
        double _value;
        public double Value
        {
            get { return _value; }
            set
            {
                _value = value;
                if (_slider != null) _slider.Value = value;
            }
        }
        public bool ShowValue { get; set; }

        public SliderElement(bool showValue, double value)
        { ShowValue = showValue; Value = value; }
        public SliderElement(string title, double value)
            : base(title, null)
        {
            Value = value;
        }
        protected override void InitCell()
        {
            var b = new Border();
            b.Padding = new Thickness(3);
            b.Margin = new Thickness(10);//, 30, 10, 30);
            b.Height = 52;
            b.Background = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
            var p = new Grid();
            p.Background = (Brush)Application.Current.Resources["PhoneBackgroundBrush"];
            var cd = new ColumnDefinition();
            cd.Width = new GridLength(60);
            p.ColumnDefinitions.Add(cd);
            cd = new ColumnDefinition();
            cd.Width = new GridLength(370);
            p.ColumnDefinitions.Add(cd);

            var l = new TextBlock();
            l.VerticalAlignment = VerticalAlignment.Center;
            l.HorizontalAlignment = HorizontalAlignment.Center;
            l.FontSize = 25;
            l.Text = ShowValue ? "" + Math.Round(Value, 1) : Title ?? "";
            _label = l;
            p.Children.Add(l);
            _slider = new Slider();
            _slider.Value = Value;
            _slider.Height = 84;
            _slider.VerticalAlignment = VerticalAlignment.Top;
            _slider.HorizontalAlignment = HorizontalAlignment.Stretch;

            _slider.ValueChanged += delegate{ // new RoutedPropertyChangedEventHandler<double>(_slider_ValueChanged);
                Value = _slider.Value;
                if (ShowValue) Title = "" + Math.Round(Value, 1);
                Trigger();
            };
            Grid.SetColumn(_slider, 1);
            p.Children.Add(_slider);
            b.Child = p;
            b.SizeChanged += (s, e) => { p.ColumnDefinitions[1].Width = new GridLength(e.NewSize.Width - 80); };
            _cell = b;
        }
    }
    public class CheckElement : Element
    {
        bool _value;
        CheckBox _cb;
        public bool Checked
        {
            get { return _value; }
            set
            {
                _value = value;
                if (_cb != null) _cb.IsChecked = value;
            }
        }
        public CheckElement(string title, bool value)
            : base(title, null)
        {
            Checked = value;
        }
        protected override void InitCell()
        {
            var b = new Border();
            b.Padding = new Thickness(3);
            b.Margin = new Thickness(10);//, 30, 10, 30);
            b.Height = 52;
            b.Background = (Brush)Application.Current.Resources["PhoneForegroundBrush"];
            var p = new Grid();
            p.Background = (Brush)Application.Current.Resources["PhoneBackgroundBrush"];
            var cd = new ColumnDefinition();
            cd.Width = new GridLength(390);
            p.ColumnDefinitions.Add(cd);
            cd = new ColumnDefinition();
            cd.Width = new GridLength(40);
            p.ColumnDefinitions.Add(cd);

            var l = new TextBlock();
            l.Text = Title ?? "";
            l.FontSize = 25;
            l.VerticalAlignment = VerticalAlignment.Center;
            l.TextAlignment = TextAlignment.Center;
            l.HorizontalAlignment = HorizontalAlignment.Center;
            _label = l;
            p.Children.Add(l);
            _cb = new CheckBox();
            _cb.IsChecked = Checked;
            _cb.Height = 76;
            _cb.Width = 70;
            _cb.VerticalAlignment = VerticalAlignment.Center;
            _cb.HorizontalAlignment = HorizontalAlignment.Center;

            _cb.Click += delegate
            {
                _value = (bool)_cb.IsChecked;
                Trigger();
            };
            Grid.SetColumn(_cb, 1);
            p.Children.Add(_cb);
            b.Child = p;
            b.SizeChanged += (s, e) => { p.ColumnDefinitions[0].Width = new GridLength(e.NewSize.Width - 50); };
            _cell = b;
        }
    }
    public class RadioElement : Element
    {
        public static Polyline Check()
        {
            var check = new Polyline();
            check.Stroke = new SolidColorBrush(Color.FromArgb(255, 150, 150, 150));
            //  UseAccentColor ? (Brush)Application.Current.Resources["PhoneAccentBrush"] :   
            check.StrokeThickness = 4;
            PointCollection pc = new PointCollection();
            pc.Add(new Point(1, 15));
            pc.Add(new Point(10, 28));
            pc.Add(new Point(25, 5));
            check.Points = pc;
            check.HorizontalAlignment = HorizontalAlignment.Center;
            check.VerticalAlignment = VerticalAlignment.Center;
            return check;
        }

        public RadioGroup Group { get;internal set; }
        Polyline _check;
        bool _checked;
        public bool Checked
        {
            get { return _checked; }
            set
            {
                if (_checked != value)
                {
                    _checked = value;
                    if (_check != null)
                    {
                        _check.Opacity = _checked ? 1 : 0;
                        if (UseAccentColor)
                            (_label as TextBlock).Foreground = _checked ? (Brush)Application.Current.Resources["PhoneAccentBrush"] :
                                                         (Brush)Application.Current.Resources["PhoneForegroundBrush"];
                    }
                }
            }
        }
        public bool UseAccentColor { get; set; }

        public RadioElement(string title) : this(title, null) { }
        public RadioElement(string title, RadioGroup group) : base(title, null) {
            UseAccentColor = true;
            if(group != null)
                group.Add(this);
        }
        protected override void InitCell() 
        {
            var b = new Button();
            var p = new Grid();
            var cd = new ColumnDefinition();
            cd.Width = new GridLength(380);
            p.ColumnDefinitions.Add(cd);
            cd = new ColumnDefinition();
            cd.Width = new GridLength(30);
            p.ColumnDefinitions.Add(cd);


            _check = Check();
            if(UseAccentColor) _check.Stroke = (Brush)Application.Current.Resources["PhoneAccentBrush"];
            _check.Opacity = _checked ? 1 : 0;

            var l = new TextBlock();
            l.Text = Title;
            l.VerticalAlignment = VerticalAlignment.Stretch;
            l.TextAlignment = TextAlignment.Center;
            if (UseAccentColor && _checked)
                l.Foreground = (Brush)Application.Current.Resources["PhoneAccentBrush"];
            _label = l;

            Grid.SetColumn(_check, 1);
            p.Children.Add(l);
            p.Children.Add(_check);

            b.Content = p;
            b.Click += new RoutedEventHandler(Trigger);
            b.SizeChanged += (s, e) => { p.ColumnDefinitions[0].Width = new GridLength(e.NewSize.Width - 75); };
            _cell = b;
        }
        public override void Trigger(object sender = null, RoutedEventArgs e = null)
        {
            if (Group != null)
                Group.Select(this);
            else Checked = !Checked;
            base.Trigger(sender, e);
        }
    }
    public class RadioGroup : IEnumerable<string> // IEnumerable<RadioElement>, 
    {
        public List<RadioElement> Elements{ get; protected set; }
        int _selected;
        public int Selected
        {
            get { return _selected; }
            set
            {
                if (Elements.Count > value && _selected != value)
                {
                    if (_selected >= 0 && Elements.Count > _selected) Elements[_selected].Checked = false;
                    if (value >= 0 && Elements.Count > value) Elements[value].Checked = true;
                }
                _selected = value;
            }
        }
        public string SelectedTitle
        {
            get { return _selected >= 0 && _selected < Elements.Count ? Elements[_selected].Title : null; }
        }
        public bool UseAccentColor { get; set; }
        public Action<RadioElement> SelectionChanged { get; set; }

        public RadioGroup(int selected)
        {
            Elements = new List<RadioElement>();
            _selected = selected;
            UseAccentColor = true;
        }
        public RadioGroup() : this(-1) { }

        public void Add(RadioElement e) {
            e.Group = this;
            if (_selected < 0)
            {
                if (e.Checked) _selected = Elements.Count;
            }
            else e.Checked = _selected == Elements.Count;
            Elements.Add(e);
        }
        public void Add(string title)
        {
            var e = new RadioElement(title, this);
            e.UseAccentColor = UseAccentColor;
        }
        internal void Select(RadioElement e)
        {
            int idx = Elements.IndexOf(e);
            if (Selected != idx)
            {
                Selected = idx;
                if (SelectionChanged != null) SelectionChanged(e);
            }
        }
        public string this[int idx]
        {
            get
            {
                if (idx < 0 || idx >= Elements.Count) return null;
                return Elements[idx].Title;
            }
            set
            {
                Elements[idx].Title = value;
            }
        }
        IEnumerator<string> IEnumerable<string>.GetEnumerator()
        {
//            return Elements.GetEnumerator();
            foreach (var e in Elements)
                yield return e.Title;
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
//            return Elements.GetEnumerator();
            foreach (var e in Elements)
                yield return e.Title;
        }
        /*        IEnumerator<RadioElement> IEnumerable<RadioElement>.GetEnumerator()
                {
                    return Elements.GetEnumerator();
                }*/
    }
    public class HtmlElement : Element
    {
//        public string Uri { get; set; }
        public HtmlElement(string title)
            : base(title, null)
        {
//            Uri = title;
        }
        public override void Trigger(object sender = null, RoutedEventArgs e = null)
        {
            if (RootElement.Current != null)
            {
                PhoneApplicationService.Current.State["ElementUri"] = _title;// Uri;
                RootElement.Current.Page.NavigationService.Navigate(new Uri("/WPDialog;component/Browser.xaml", UriKind.Relative));
            }
            base.Trigger(sender, e);
        }
    }
    public class LinkElement : Element
    {
        HyperlinkButton _button;
        public override string Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value;
                if (_button != null)
                {
                    _button.Content = _title;
                    _button.NavigateUri = new Uri(_title, UriKind.Absolute);
                }
            }
        }
        public LinkElement(string title)
            : base(title, null)
        { }
        protected override void InitCell()
        {
            _button = new HyperlinkButton();
            _button.Content = _title;
            _button.NavigateUri = new Uri(_title, UriKind.Absolute);
            _button.TargetName = "Explorer";
            _button.Height = 52;
            _cell = _button;
        }
    }
    public class RootElement : Element, IEnumerable<Element>
    {
        static int idx = 0;
        public static RootElement Current { get; protected set; }
        public static Polyline Arrow()
        {
            var pline = new Polyline();
            pline.Stroke = new SolidColorBrush(Color.FromArgb(255, 150, 150, 150));
            pline.StrokeThickness = 4;

            PointCollection pc = new PointCollection();
            pc.Add(new Point(1, 5));
            pc.Add(new Point(12, 17));
            pc.Add(new Point(1, 29));
            pline.Points = pc;
            pline.HorizontalAlignment = HorizontalAlignment.Center;
            pline.VerticalAlignment = VerticalAlignment.Center;
            return pline;
        }

        protected List<Element> Elements { get; set; }
        public DialogPage Page { get; protected set; }
        public Action<RootElement> Popped { get; set; }
        public ApplicationBar ApplicationBar { get; set; }// { if (Page != null) Page.ApplicationBar = value; }

#if Toolkit
        public TransitionElement Transition = new SlideTransition() { Mode = SlideTransitionMode.SlideLeftFadeIn };
        public TransitionElement BackTransition = new SlideTransition() { Mode = SlideTransitionMode.SlideRightFadeIn };
#endif

        public RootElement(string title) :
            base(title, null)
        {
            Elements = new List<Element>();
        }
        public override void Trigger(object sender = null, RoutedEventArgs e = null)
        {
            Push(null);
            base.Trigger(sender, e);
        }
        protected override void InitCell()
        {
            var b = new Button();
            var p = new Grid();
            var cd = new ColumnDefinition();
            cd.Width = new GridLength(380);//, GridUnitType.Star);
            p.ColumnDefinitions.Add(cd);
            cd = new ColumnDefinition();
            cd.Width = new GridLength(20);
            p.ColumnDefinitions.Add(cd);
            p.VerticalAlignment = VerticalAlignment.Stretch;

            var l = new TextBlock();
            l.Text = Title;
            l.VerticalAlignment = VerticalAlignment.Stretch;
            l.TextAlignment = TextAlignment.Center;
            _label = l;

            var arr = Arrow();
            Grid.SetColumn(arr, 1);
            p.Children.Add(l);
            p.Children.Add(arr);

            b.Content = p;
            b.VerticalContentAlignment = VerticalAlignment.Stretch;
            b.Click += new RoutedEventHandler(Trigger);
            b.SizeChanged += (s, e) => { p.ColumnDefinitions[0].Width = new GridLength(e.NewSize.Width - 80); };
            _cell = b;
        }

        public void Push(NavigationService navi = null)
        {
            if(navi == null && Current != null && Current.Page != null) navi = Current.Page.NavigationService;
            if (navi != null)
            {
                if (Parent == null) Parent = Current;
                Current = this;
                bool b = navi.Navigate(new Uri("/WPDialog;component/DialogPage.xaml?stack=root" + ++idx, UriKind.Relative));
            }
        }
        public void Pop()
        {
            if (Current != this || Page == null) return;
            Page.NavigationService.GoBack();
        }
        internal void RemoveSelf()
        {
            Page.Content.Children.Clear();
            Page = null;
            Current = Parent;
            if (Popped != null) Popped(this);
        }
        public void ApplyTo(DialogPage page)
        {
            Page = page;
            page.Title = Title;
            foreach (var e in Elements)
                page.Content.Children.Add(e.Cell);

            if (ApplicationBar != null)
                page.ApplicationBar = ApplicationBar;
        }

        public void SetDefaultApplicationBar(EventHandler done, EventHandler cancel)
        {
            var ab = new ApplicationBar();
            ab.IsVisible = true;
            if (done != null)
            {
                var b = new ApplicationBarIconButton(new Uri("/Toolkit.Content/ApplicationBar.Check.png", UriKind.Relative));
                b.Text = "done";
                b.Click += done;
                ab.Buttons.Add(b);
            }
            if (cancel != null)
            {
                var b = new ApplicationBarIconButton(new Uri("/Toolkit.Content/ApplicationBar.Cancel.png", UriKind.Relative));
                b.Text = "cancel";
                b.Click += cancel;
                ab.Buttons.Add(b);
            }
            ApplicationBar = ab;
            if(Page != null) Page.ApplicationBar = ab;
        }
        public void Add(Element e)
        {
            Elements.Add(e);
            e.Parent = this;
            if (Page != null) Page.Content.Children.Add(e.Cell);
        }
        public void Add(RadioGroup g, string header = null, string footer = null)
        {
            if (header != null) Add(new SeparatorElement(header));
            foreach (var e in g.Elements) Add(e);
            if (footer != null) Add(new SeparatorElement(footer, SeparatorStyle.Footer));
        }
        public void InsertAt(Element e, int index)
        {
            if (index >= Elements.Count || index < 0) Add(e);
            else
            {
                Elements.Insert(index, e);
                e.Parent = this;
                if (Page != null) Page.Content.Children.Insert(index, e.Cell);
            }
        }
        public int IndexOf(Element e)
        {
            return e.Parent == this ? Elements.IndexOf(e) : -1;
        }
        public void RemoveAt(int index) {
            if (index >= Elements.Count || index < 0) throw new ArgumentException("index is out of range");
            var e = Elements[index];
            Elements.RemoveAt(index);
            e.Parent = null;
            if (Page != null) Page.Content.Children.RemoveAt(index);
        }
        public void Remove(Element e)
        {
            if (e.Parent != this || Elements.IndexOf(e) < 0) throw new ArgumentException("not a child of this");
            Elements.Remove(e);
            e.Parent = null;
            if (Page != null) Page.Content.Children.Remove(e.Cell);
        }
        public int Count
        {
            get
            {
                return Elements.Count;
            }
        }

        public Element this[int idx]
        {
            get
            {
                return Elements[idx];
            }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return Elements.GetEnumerator();
        }
        IEnumerator<Element> IEnumerable<Element>.GetEnumerator()
        {
            return Elements.GetEnumerator();
        }
    }
}
